package com.example.myapplication.found.model;

import com.example.myapplication.found.IndustryFound;
import com.example.myapplication.found.IndustryList;
import com.example.myapplication.found.bean.BuyPointBean;
import com.example.myapplication.found.bean.KDJBean;
import com.example.myapplication.found.bean.KDJOrderBean;
import com.example.myapplication.found.bean.StockBean;
import com.example.myapplication.found.bean.StockKlineBean;
import com.example.myapplication.found.bean.StockNameBean;
import com.example.myapplication.found.bean.StockOrderBean;
import com.example.myapplication.found.contract.KdjCallback;
import com.example.myapplication.found.contract.KdjLoopCallback;
import com.example.myapplication.found.contract.ZiHangCallback;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author: rongfeng
 * @date: 2022/2/14
 * @description
 */

/**
 * 计算方法编辑
 * KDJ的计算比较复杂，首先要计算周期（n日、n周等）的RSV值，即未成熟随机指标值，然后再计算K值、D值、J值等。以n日KDJ数值的计算为例，其计算公式为
 * n日RSV=（Cn－Ln）/（Hn－Ln）×100
 * 公式中，Cn为第n日收盘价；Ln为n日内的最低价；Hn为n日内的最高价。
 * 其次，计算K值与D值：
 * 当日K值=2/3×前一日K值+1/3×当日RSV
 * 当日D值=2/3×前一日D值+1/3×当日K值
 * 若无前一日K 值与D值，则可分别用50来代替。
 * J值=3*当日K值-2*当日D值
 * 以9日为周期的KD线为例，即未成熟随机值，计算公式为
 * 9日RSV=（C－L9）÷（H9－L9）×100
 * 公式中，C为第9日的收盘价；L9为9日内的最低价；H9为9日内的最高价。
 * K值=2/3×第8日K值+1/3×第9日RSV
 * D值=2/3×第8日D值+1/3×第9日K值
 * J值=3*第9日K值-2*第9日D值
 * 若无前一日K
 * 值与D值，则可以分别用50代替。
 */

public class KDJ {
    public static final int type = 3;
    private StockBean dayStockBean;
    private StockBean weekStockBean;
    public List<StockKlineBean> dayKlineList;
    public List<StockKlineBean> weekKlineList;

    public List<StockKlineBean> weekOverlayKlineList;
    //9, 18,24
    private int n = 9;

    private KdjCallback kdjCallback;
    public List<KDJBean> kdjBeanList;
    List<KDJBean> weekKdjBeanList;
    List<StockOrderBean> orderList = new ArrayList<>();
    ExecutorService pool;
    BuyPointBean buyPointBean;
    public List<StockNameBean> stockNameBeanList;
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");


    public KDJ(StockBean dayStockBean, StockBean weekStockBean) {
        this.dayStockBean = dayStockBean;
        this.weekStockBean = weekStockBean;
        this.dayKlineList = dayStockBean.klineList;
        if (weekStockBean != null) {
            this.weekKlineList = weekStockBean.klineList;
        }
        pool = Executors.newFixedThreadPool(1);

        getWeekKline();
        initKDJ();
    }

    public KdjCallback getKdjCallback() {
        return kdjCallback;
    }

    public void setKdjCallback(KdjCallback kdjCallback) {
        this.kdjCallback = kdjCallback;
    }


    public void start() {
//        loop();
        dayDeal2();

//        findStock();
//        statistics();

    }


    public KDJBean getKDJ(String date) {
        KDJBean kdjBean = null;
        for (KDJBean bean : kdjBeanList) {
            if (bean.stockKlineBean.date.equals(date)) {
                kdjBean = bean;
            }
        }
        return kdjBean;
    }



    public KDJBean isBKWeekKdj(String date) {
        boolean isWeekPoint = false;
        KDJBean kdjBean = null;
        for (int i = 0; i < weekKdjBeanList.size(); i++) {
            KDJBean weekKdj = weekKdjBeanList.get(i);
            if (weekKdj.stockKlineBean.week == 5 &&
                    date.equals(weekKdj.stockKlineBean.date)) {
                kdjBean = weekKdj;
                if (kdjBean.j > kdjBean.k && kdjBean.k > kdjBean.d
                        && kdjBean.j > 90) {
                    isWeekPoint = true;
                }
                break;
            }
        }
        if (isWeekPoint) {
            return kdjBean;
        }
        return null;
    }


    public boolean checkCGL(String date) {
        for (int i = 1; i < dayStockBean.klineList.size(); i++) {
            StockKlineBean klineBean = dayStockBean.klineList.get(i);
            if (date.equals(klineBean.date)) {
                StockKlineBean lastKlineBean = dayStockBean.klineList.get(i - 1);
                if (
//                        klineBean.chengjiaoliang - lastKlineBean.chengjiaoliang > 0
//                        &&
                        klineBean.chengjiaoliang / lastKlineBean.chengjiaoliang < 1.2
                ) {
                    return true;
                }
            }
        }
        return false;
    }


    public int index = 0;

    private void loop() {
        pool.execute(new Runnable() {
            @Override
            public synchronized void run() {
                if (index < kdjBeanList.size()) {
                    dayDeal(kdjBeanList.get(index));
                    index++;
                } else {
                    statistics();
                }
            }
        });
    }


    int lastDealDay = 0;

    public void dayDeal(KDJBean kdjBean) {
        int dealDay = 1;
        KDJBean dayKdj = null;
        KDJBean weekKdj = null;

        dayKdj = isBKDayKdj(kdjBean.stockKlineBean.date);
//        weekKdj = isBKWeekKdj(kdjBean.stockKlineBean.date);

        boolean isToday = kdjBean.stockKlineBean.date.equals(kdjBeanList.get(kdjBeanList.size() - 1).stockKlineBean.date);
        boolean maxPrice = kdjBean.stockKlineBean.shouPan < 100;
        boolean isZT = kdjBean.stockKlineBean.chg < 9;
//        boolean iscjl = checkCGL(kdjBean.stockKlineBean.date);

        if (dayKdj != null
//                && isToday
//                && maxPrice
//                && weekKdj != null
//                && isZT
//                && iscjl
        ) {
            buyPointBean = new BuyPointBean();
            buyPointBean.date = kdjBean.stockKlineBean.date;
            buyPointBean.code = dayStockBean.code;
            buyPointBean.name = dayStockBean.name;
            buyPointBean.dayKdj = kdjBean;
            buyPointBean.weekKdj = weekKdj;
            buyPointBean.shouPan = kdjBeanList.get(kdjBeanList.size() - 1).stockKlineBean.shouPan;
        }

        if (kdjBean.stockKlineBean.index > lastDealDay
                && dayKdj != null
//                && maxPrice
//                && weekKdj != null
//                && isZT
//                && iscjl
        ) {
            float money = 10000;
            boolean lastChgUP = false;
            String date = null;
            for (int j = 0; j < dealDay; j++) {
                int nextIndex = kdjBean.stockKlineBean.index + j + 1;
                if (nextIndex < dayKlineList.size() && !lastChgUP) {
                    StockKlineBean nextBean = dayKlineList.get(nextIndex);
                    lastChgUP = nextBean.chg > 0;
                    money = money + (money * (nextBean.chg / 100));
                    lastDealDay = nextIndex;
                    if (date == null) {
                        date = nextBean.date;
                    }
                    IndustryFound.date.add(nextBean.date);
                    if (IndustryFound.isTest) {
                        System.out.println(dayStockBean.name + " " + dayStockBean.code + " " + nextBean.date + " 涨跌 " + nextBean.chg + " money " + money);
                    }
                }
            }
            StockOrderBean orderBean = new StockOrderBean();
            orderBean.average = money;
            orderBean.zhang = money > 10000;
            orderBean.die = money < 10000;
            orderBean.pin = money == 10000;
            orderBean.money = money;
            orderList.add(orderBean);
            if (IndustryFound.isTest) {
//                System.out.println();
            }
            if (dayStockBean.code.startsWith("BK") && date != null) {
//                IndustryList industryFound = new IndustryList(dayStockBean.code, dayStockBean.name, date);
//                industryFound.setZiHangCallback(ziHangCallback);
//                industryFound.setOrderBean(orderBean);
//                industryFound.getStockList();
                loop();
            } else {
                loop();
            }
        } else {
            loop();
        }
    }


    public KDJBean isBKDayKdj(String date) {
        boolean isWeekPoint = false;
        KDJBean kdjBean = null;
        for (int i = 0; i < kdjBeanList.size(); i++) {
            KDJBean dayKdj = kdjBeanList.get(i);
            if (date.equals(dayKdj.stockKlineBean.date)) {

                if (stockNameBeanList != null) {
                    String fundName = "";
                    for (StockNameBean stockNameBean : stockNameBeanList) {
                        if (dayKdj.stockKlineBean.date2 >= stockNameBean.getDate()) {
                            fundName = stockNameBean.getCHANGE_AFTER_FN();
                            break;
                        }
                    }
                    if (fundName.contains("ST") || fundName.startsWith("退市") || fundName.startsWith("S")) {
                        return null;
                    }

                }

                kdjBean = dayKdj;
                if (
                        dayKdj.stockKlineBean.chg < 9.5
//                      &&  kdjBean.j > kdjBean.k && kdjBean.k > kdjBean.d
                                && kdjBean.d < 10) {
                    isWeekPoint = true;
                }
                break;
            }
        }


        if (isWeekPoint) {
            return kdjBean;
        }
        return null;
    }

    public void dayDeal2() {

        List<List<KDJBean>> dealList = new ArrayList<>();

        for (int i = 0; i < kdjBeanList.size(); i++) {
            KDJBean kdjBean = kdjBeanList.get(i);
            KDJBean dayKdj = isBKDayKdj(kdjBean.stockKlineBean.date);
            if (dayKdj != null) {
                List<KDJBean> dealList2 = new ArrayList<>();
                for (int j = i; j < kdjBeanList.size(); j++) {
                    KDJBean kdjBean2 = kdjBeanList.get(j);
                    KDJBean dayKdj2 = isBKDayKdj(kdjBean2.stockKlineBean.date);
                    if (dayKdj2 != null) {
                        i = j;
                        dealList2.add(dayKdj2);
                    } else {
                        dealList2.add(kdjBean2);
                        break;
                    }
                }
                if (dealList2.size() > 1) {
                    dealList.add(dealList2);
                }
            }
        }


        for (int j = 0; j < dealList.size(); j++) {
            List<KDJBean> dealList2 = dealList.get(j);
            float money = 10000;
            for (int i = 1; i < dealList2.size(); i++) {
                KDJBean kdjBean = dealList2.get(i);
                money = money + (money * (kdjBean.stockKlineBean.chg / 100));

                if (IndustryFound.isTest) {
                    System.out.println(kdjBean.stockKlineBean.date + " 涨跌 " + kdjBean.stockKlineBean.chg);
                }

                break;
            }

            StockOrderBean orderBean = new StockOrderBean();
            orderBean.average = money;
            orderBean.zhang = money > 10000;
            orderBean.die = money < 10000;
            orderBean.pin = money == 10000;
            orderBean.money = money;
            orderBean.dealDay = dealList2.size();
            orderList.add(orderBean);

            System.out.println(" money2 " + money);
            System.out.println();
        }
        findStock();
        statistics();

//        System.out.println();
//
//        if (kdjCallback != null) {
//            kdjCallback.kdjCallback(null, buyPointBean);
//        }
    }

    private void findStock() {
        KDJBean kdjBean = isBKDayKdj(kdjBeanList.get(kdjBeanList.size() - 1).stockKlineBean.date);
        if (kdjBean != null) {
            buyPointBean = new BuyPointBean();
            buyPointBean.date = kdjBean.stockKlineBean.date;
            buyPointBean.code = dayStockBean.code;
            buyPointBean.name = dayStockBean.name;
            buyPointBean.dayKdj = kdjBean;
            buyPointBean.shouPan = kdjBeanList.get(kdjBeanList.size() - 1).stockKlineBean.shouPan;
        }
    }

    ZiHangCallback ziHangCallback = new ZiHangCallback() {
        @Override
        public void ziHangCallback(List<KDJOrderBean> list) {
            loop();
        }
    };


    public KDJBean isWeekKdj(String date) {
        boolean isWeekPoint = false;
        KDJBean kdjBean = null;
        for (int i = 0; i < weekKdjBeanList.size(); i++) {
            KDJBean weekKdj = weekKdjBeanList.get(i);
            if (weekKdj.stockKlineBean.week == 3 && date.equals(weekKdj.stockKlineBean.date)) {
                kdjBean = weekKdj;
                if (kdjBean.j > kdjBean.k && kdjBean.k > kdjBean.d
                        && kdjBean.j > 90) {
                    isWeekPoint = true;
                }
                break;
            }
        }
        if (isWeekPoint) {
            return kdjBean;
        }
        return null;
    }


    public void statistics() {
        float orderZhang = 0, orderDie = 0, orderPin = 0, orderAvg = 0, money = 0;
        int dealDay = 0;
        for (StockOrderBean orderBean : orderList) {
            if (orderBean.zhang) {
                orderZhang++;
            }
            if (orderBean.die) {
                orderDie++;
            }
            if (orderBean.pin) {
                orderPin++;
            }
            money = money + orderBean.money;
            orderAvg = orderAvg + orderBean.average;
            dealDay += orderBean.dealDay;
        }
        float rate = 0;
        KDJOrderBean kdjOrderBean = null;
        if (orderList.size() > 0) {
            rate = (orderZhang / orderList.size()) * 100;
            float moneyAvg = money / orderList.size();
            dealDay = dealDay / orderList.size();
            if (IndustryFound.isTest) {
                System.out.println(dayStockBean.name + " " + dayStockBean.code);
                System.out.println("买入次数 " + orderList.size());
                System.out.println("买入涨 " + orderZhang + " 跌 " + orderDie + "  平 " + orderPin + " 胜率 " + rate + "%");
                System.out.println("平均剩余金额 " + moneyAvg);
                System.out.println("平均交易天数 " + dealDay);
                System.out.println();
            }
            kdjOrderBean = new KDJOrderBean();
            kdjOrderBean.moneyAvg = moneyAvg;
            kdjOrderBean.rate = rate;
            kdjOrderBean.orderZhang = orderZhang;
            kdjOrderBean.orderDie = orderDie;
            kdjOrderBean.orderPin = orderPin;
            kdjOrderBean.orderList = orderList;
            kdjOrderBean.dealDay = dealDay;
            kdjOrderBean.name = dayStockBean.name;
            kdjOrderBean.code = dayStockBean.code;
        }

        if (kdjCallback != null) {
            kdjCallback.kdjCallback(kdjOrderBean, buyPointBean);
        }
    }

    public void initKDJ() {
        Calendar cal = Calendar.getInstance(); // 获得一个日历
        kdjBeanList = new ArrayList<>();
        float lastK = 50;
        float lastD = 50;
        for (int i = n; i <= dayKlineList.size(); i++) {
            List<StockKlineBean> tempList = dayKlineList.subList(i - n, i);
            float H = -1, L = 999999999;
            float C = tempList.get(n - 1).shouPan;
            for (StockKlineBean ben : tempList) {
                if (ben.zuigao > H) {
                    H = ben.zuigao;
                }
                if (ben.zuidi < L) {
                    L = ben.zuidi;
                }
            }
            float rsv = (C - L) / (H - L) * 100;
            lastK = getKValue(rsv, lastK);
            lastD = getDValue(lastD, lastK);
            float J = 3f * lastK - 2 * lastD;
            if (IndustryFound.isTest) {
//                System.out.println(weekKlineList.get(i - 1).date);
//                System.out.println(" k=" + (int) lastK + " d=" + (int) lastD + " j=" + (int) J);
            }
            kdjBeanList.add(new KDJBean(dayKlineList.get(i - 1), lastK, lastD, J));
        }

        weekKdjBeanList = new ArrayList<>();
        lastK = 50;
        lastD = 50;
        if (weekKlineList != null) {
            for (int i = n; i <= weekKlineList.size(); i++) {
                List<StockKlineBean> tempList = weekKlineList.subList(i - n, i);
                float H = -1, L = 999999999;
                StockKlineBean tempStockKlineBean = tempList.get(n - 1);
                float C = tempStockKlineBean.shouPan;
                for (StockKlineBean ben : tempList) {
                    if (ben.zuigao > H) {
                        H = ben.zuigao;
                    }
                    if (ben.zuidi < L) {
                        L = ben.zuidi;
                    }
                }
                float rsv = (C - L) / (H - L) * 100;
                lastK = getKValue(rsv, lastK);
                lastD = getDValue(lastD, lastK);
                float J = 3f * lastK - 2 * lastD;
                if (IndustryFound.isTest) {
//                System.out.println(weekKlineList.get(i - 1).date + " k=" + (int) lastK + " d=" + (int) lastD + " j=" + (int) J + " * ");
//                System.out.println();
                }
                Date datet = null;
                try {
                    datet = format.parse(tempStockKlineBean.date);
                    cal.setTime(datet);
                } catch (ParseException e) {
                    e.printStackTrace();
                }
                int w = cal.get(Calendar.DAY_OF_WEEK) - 1; // 指示一个星期中的某天。
                if (w < 0) {
                    w = 0;
                }

                tempStockKlineBean.week = w;
                KDJBean kdjBean = new KDJBean(tempStockKlineBean, lastK, lastD, J);
                weekKdjBeanList.add(kdjBean);
                dayWeekKdj(lastK, lastD, tempList);
            }
        }
//        for (KDJBean kdjBean :weekKdjBeanList ) {
//            System.out.println(kdjBean.stockKlineBean.date + " k=" + (int) kdjBean.k + " d=" + (int) kdjBean.d + " j=" + (int) kdjBean.j );
//            System.out.println();
//        }
    }

    private void dayWeekKdj(float weekK, float weekD, List<StockKlineBean> tempList) {
        int lastDay = 0;
        StockKlineBean tempStockKlineBean = tempList.get(n - 1);
        StockKlineBean lastStockKlineBean = null;
        for (StockKlineBean stockKlineBean : weekOverlayKlineList) {
            if (stockKlineBean.date.equals(tempList.get(n - 1).date)) {
                lastStockKlineBean = stockKlineBean;
                break;
            }
        }
        if (lastStockKlineBean == null) {
            return;
        }
        int index = weekOverlayKlineList.indexOf(lastStockKlineBean) + 1;
        if (index < weekOverlayKlineList.size()) {
            lastStockKlineBean = weekOverlayKlineList.get(index);
        }

        boolean isAdd = false;
        while (lastStockKlineBean.week > lastDay) {


            if (lastStockKlineBean != null) {
                tempList.set(n - 1, lastStockKlineBean);
            }

            float H = -1, L = 999999999;
            float C = lastStockKlineBean.shouPan;
            for (StockKlineBean ben : tempList) {
                if (ben.zuigao > H) {
                    H = ben.zuigao;
                }
                if (ben.zuidi < L) {
                    L = ben.zuidi;
                }
            }

            float rsv = (C - L) / (H - L) * 100;
            float lastK = getKValue(rsv, weekK);
            float lastD = getDValue(weekD, lastK);
            float J = 3f * lastK - 2 * lastD;
            if (IndustryFound.isTest) {
//                System.out.println(lastStockKlineBean.date + " k=" + (int) lastK + " d=" + (int) lastD + " j=" + (int) J);
//                System.out.println();
            }
            KDJBean kdjBean = new KDJBean(lastStockKlineBean, lastK, lastD, J);
            weekKdjBeanList.add(kdjBean);
            isAdd = true;
            index++;
            lastDay = lastStockKlineBean.week;
            if (index < weekOverlayKlineList.size()) {
                lastStockKlineBean = weekOverlayKlineList.get(index);
            } else {
                break;
            }
        }
        if (isAdd) {
            weekKdjBeanList.remove(weekKdjBeanList.size() - 1);
        }
        tempList.set(n - 1, tempStockKlineBean);
    }


    private void getWeekKline() {
        List<StockKlineBean> weekList = new ArrayList<>();

        weekOverlayKlineList = new ArrayList<>();

        Calendar cal = Calendar.getInstance(); // 获得一个日历

        int lastDay = 0;
        for (StockKlineBean stockKlineBean : dayKlineList) {
            Date datet = null;
            try {
                datet = format.parse(stockKlineBean.date);
                cal.setTime(datet);
            } catch (ParseException e) {
                e.printStackTrace();
            }
            int w = cal.get(Calendar.DAY_OF_WEEK) - 1; // 指示一个星期中的某天。
            if (w < 0) {
                w = 0;
            }

            if (w <= lastDay) {
                weekList.clear();
            }
            lastDay = w;
            weekList.add(stockKlineBean);
            StockKlineBean weekKline = new StockKlineBean();
            if (weekList.size() > 1) {
                StockKlineBean stockKlineBean1 = weekList.get(0);
                StockKlineBean stockKlineBean2 = weekList.get(weekList.size() - 1);
                float H = -1, L = 999999999;
                for (StockKlineBean ben : weekList) {
                    if (ben.zuigao > H) {
                        H = ben.zuigao;
                    }
                    if (ben.zuidi < L) {
                        L = ben.zuidi;
                    }
                }
                weekKline.date = stockKlineBean.date;
                weekKline.shouPan = stockKlineBean2.shouPan;
                weekKline.zuigao = H;
                weekKline.zuidi = L;
            } else {
                weekKline = stockKlineBean;
            }
            weekKline.week = w;
            weekOverlayKlineList.add(weekKline);
        }
    }

    public float getKValue(float rsv, float k) {
        return 2f / 3f * k + 1f / 3f * rsv;
    }

    public float getDValue(float d, float k) {
        return 2f / 3f * d + 1f / 3f * k;
    }


    private static double median(List<Double> total) {
        double j = 0;
        //集合排序
        Collections.sort(total);
        int size = total.size();
        if (size % 2 == 1) {
            j = total.get((size - 1) / 2);
        } else {
            //加0.0是为了把int转成double类型，不然除以2会算错
            j = (total.get(size / 2 - 1) + total.get(size / 2) + 0.0) / 2;
        }
        return j;
    }
}
